package com.zx1316.javasimpledoudizhu.server;

import com.zx1316.javasimpledoudizhu.misc.Card;
import javafx.application.Platform;

import java.util.*;

import static com.zx1316.javasimpledoudizhu.DDZApplication.INIT_CARD;

// 这个类处理游戏流程
public class GameThread extends Thread {
    private final Server server;
    private final Vector<Vector<Card>> cardsList = new Vector<>();
    private final Vector<Vector<String>> records = new Vector<>();
    private final String[] playerList = new String[3];
    private final int[] scores = {0, 0, 0};
    private int[] needDouble;
    private int[] discardTimes;
    private int points;
    private int waitingFor;
    private int rounds = 1;
    private boolean isPause;
    private boolean isInGame;

    public GameThread(Server server) {
        this.server = server;
        for (int i = 0; i < 3; i++) {
            cardsList.add(new Vector<>());
            records.add(new Vector<>());
        }
    }

    @Override
    public void run() {
        int winner = 0;
        boolean callFlag = true;
        while (rounds <= server.getTotalRounds()) {
            for (int i = 0; i < 3; i++) {
                records.get(i).clear();
            }
            if (callFlag) {
                // 当某一ClientThread发现都准备后解锁
                synchronized (server.getLock()) {
                    try {
                        server.getLock().wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            for (int i = 0; i < 3; i++) {
                sendMessage("START@" + rounds, i, true);
                if (Server.getClients()[i] != null) {
                    Server.getClients()[i].setReady(false);
                }
            }
            ArrayList<String> remainingCards = new ArrayList<>(List.of(INIT_CARD));
            Random random = new Random();
            for (int i = 0; i < 3; i++) {
                cardsList.get(i).clear();
                for (int j = 0; j < 17; j++) {
                    int rand = random.nextInt(remainingCards.size());
                    cardsList.get(i).add(new Card(remainingCards.get(rand)));
                    remainingCards.remove(rand);
                }
                cardsList.get(i).sort(Comparator.reverseOrder());
            }
            for (int i = 0; i < 3; i++) {
                StringBuilder sb = new StringBuilder("CARD");
                for (Card card : cardsList.get(i)) {
                    sb.append("@");
                    sb.append(card.content());
                }
                sendMessage(sb.toString(), i, true);
            }
            int[] call = new int[3];
            for (int i = 0; i < 3; i++) {
                int j = (winner + i) % 3;
                if (Server.getClients()[j] == null) {
                    waitingFor = j;
                    isPause = true;
                    try {
                        synchronized (server.getLock()) {
                            server.getLock().wait();
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException();
                    }
                }
                isPause = false;
                sendMessage("CALL?", j, true);
                try {
                    synchronized (server.getLock()) {
                        server.getLock().wait();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException();
                }
                records.get(j).remove("CALL?");
                call[j] = points;
                if (points == 3) {
                    break;
                }
            }
            if (call[0] != 0 || call[1] != 0 || call[2] != 0) {
                int landlord;
                int counter = 0;
                if (call[0] > call[1] && call[0] > call[2]) {
                    points = call[0];
                    landlord = 0;
                } else if (call[1] > call[0] && call[1] > call[2]) {
                    points = call[1];
                    landlord = 1;
                } else {
                    points = call[2];
                    landlord = 2;
                }
                for (String str : remainingCards) {
                    cardsList.get(landlord).add(new Card(str));
                }
                cardsList.get(landlord).sort(Comparator.reverseOrder());
                for (int i = 0; i < 3; i++) {
                    sendMessage("LANDLORD@" + points + "@" + remainingCards.get(0) + "@" + remainingCards.get(1) + "@" + remainingCards.get(2) + "@" + playerList[landlord], i, true);
                }
                if (server.isAllowDouble()) {
                    needDouble = new int[]{-1, -1, -1};
                    for (int i = 0; i < 3; i++) {
                        sendMessage("DOUBLE?", i, true);
                    }
                    synchronized (server.getLock()) {
                        try {
                            server.getLock().wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    for (Vector<String> strList : records) {
                        strList.remove("DOUBLE?");
                    }
                } else {
                    needDouble = new int[]{0, 0, 0};
                }
                discardTimes = new int[]{0, 0, 0};
                while (!cardsList.get(0).isEmpty() && !cardsList.get(1).isEmpty() && !cardsList.get(2).isEmpty()) {
                    if (Server.getClients()[(landlord + counter) % 3] == null) {
                        waitingFor = (landlord + counter) % 3;
                        isPause = true;
                        synchronized (server.getLock()) {
                            try {
                                server.getLock().wait();
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }
                    isPause = false;
                    sendMessage("PLAY", (landlord + counter) % 3, true);
                    synchronized (server.getLock()) {
                        try {
                            server.getLock().wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    records.get((landlord + counter) % 3).remove("PLAY");
                    counter++;
                }
                for (winner = 0; !cardsList.get(winner).isEmpty(); winner++) ;
                if (discardTimes[(landlord + 1) % 3] == 0 && discardTimes[(landlord + 2) % 3] == 0 || discardTimes[landlord] == 1 && winner != landlord) {
                    points *= 2;
                    for (int i = 0; i < 3; i++) {
                        sendMessage("BOMB@false", i, false);
                    }
                }
                for (int i = 0; i < 3; i++) {
                    if (!cardsList.get(i).isEmpty()) {
                        StringBuilder sb = new StringBuilder("REMAINING");
                        for (Card card : cardsList.get(i)) {
                            sb.append("@");
                            sb.append(card.content());
                        }
                        sb.append("@");
                        sb.append(playerList[i]);
                        for (int j = 0; j < 3; j++) {
                            sendMessage(sb.toString(), j, false);
                        }
                    }
                }
                if (winner == landlord) {
                    scores[landlord] += points * ((1 + needDouble[(landlord + 1) % 3]) + (1 + needDouble[(landlord + 2) % 3])) * (1 + needDouble[landlord]);
                    scores[(landlord + 1) % 3] -= points * (1 + needDouble[(landlord + 1) % 3]) * (1 + needDouble[landlord]);
                    scores[(landlord + 2) % 3] -= points * (1 + needDouble[(landlord + 2) % 3]) * (1 + needDouble[landlord]);
                } else {
                    scores[landlord] -= points * ((1 + needDouble[(landlord + 1) % 3]) + (1 + needDouble[(landlord + 2) % 3])) * (1 + needDouble[landlord]);
                    scores[(landlord + 1) % 3] += points * (1 + needDouble[(landlord + 1) % 3]) * (1 + needDouble[landlord]);
                    scores[(landlord + 2) % 3] += points * (1 + needDouble[(landlord + 2) % 3]) * (1 + needDouble[landlord]);
                }
                for (int i = 0; i < 3; i++) {
                    sendMessage("SCORE@" + playerList[0] + "@" + scores[0] + "@" + playerList[1] + "@" + scores[1] + "@" + playerList[2] + "@" + scores[2], i, false);
                }
                callFlag = true;
                rounds++;
            } else {
                winner = (winner + 1) % 3;
                callFlag = false;
            }
        }
        Platform.runLater(() -> {
            server.getController().name1.setText("");
            server.getController().name2.setText("");
            server.getController().name3.setText("");
            server.getController().playerConfig.setDisable(false);
            server.getController().gameConfig.setDisable(false);
            server.getController().mainConfig.setDisable(false);
            server.getController().start.setDisable(false);
            server.getController().stop.setDisable(true);
        });
        server.stopServer(false);
    }

    private void sendMessage(String message, int id, boolean record) {
        if (Server.getClients()[id] != null) {
            System.out.println("Server send " + message + " to id " + id + " name " + playerList[id]);
            Server.getClients()[id].getWriter().println(message);
            Server.getClients()[id].getWriter().flush();
        }
        if (record) {
            records.get(id).add(message);
        }
    }

    public int getPoints() {
        return points;
    }

    public void setPoints(int points) {
        this.points = points;
    }

    public Vector<Vector<Card>> getCardsList() {
        return cardsList;
    }

    public int[] getNeedDouble() {
        return needDouble;
    }

    public int[] getScores() {
        return scores;
    }

    public int[] getDiscardTimes() {
        return discardTimes;
    }

    public Vector<Vector<String>> getRecords() {
        return records;
    }

    public int getWaitingFor() {
        return waitingFor;
    }

    public boolean isPause() {
        return isPause;
    }

    public boolean isInGame() {
        return isInGame;
    }

    public void setInGame(boolean inGame) {
        isInGame = inGame;
    }

    public String[] getPlayerList() {
        return playerList;
    }

    public int getRounds() {
        return rounds;
    }
}
